<template>
  <div class="scene-wrap">
    <canvas ref="sceneDom" :width="innerWidth" :height="innerHeight" />
  </div>
  <div>
    <h3>顶点着色器</h3> 每次渲染一个形状时，顶点着色器会在形状中的每个顶点运行。它的工作是将输入顶点从原始坐标系转换到 WebGL 使用的裁剪空间坐标系，其中每个轴的坐标范围从 -1.0 到
    1.0，并且不考虑纵横比，实际尺寸或任何其他因素。 顶点着色器需要对顶点坐标进行必要的转换，在每个顶点基础上进行其他调整或计算，然后通过将其保存在由 GLSL 提供的特殊变量（我们称为 gl_Position）中来返回变换后的顶点
    顶点着色器根据需要，也可以完成其他工作。例如，决定哪个包含 texel 面部纹理的坐标，可以应用于顶点；通过法线来确定应用到顶点的光照因子等。然后将这些信息存储在变量（varyings)或属性
    (attributes)属性中，以便与片段着色器共享 <h3>片段着色器</h3>
    片段着色器在顶点着色器处理完图形的顶点后，会被要绘制的每个图形的每个像素点调用一次。它的职责是确定像素的颜色，通过指定应用到像素的纹理元素（也就是图形纹理中的像素），获取纹理元素的颜色，然后将适当的光照应用于颜色。之后颜色存储在特殊变量
    gl_FragColor 中，返回到 WebGL 层。该颜色将最终绘制到屏幕上图形对应像素的对应位置。
  </div>
</template>
<script setup lang="ts">
import { mat4 } from 'gl-matrix'
import { useCommonStore } from '@/stores/common'
const { innerWidth, innerHeight } = storeToRefs(useCommonStore())
import {
  initShaderProgram,
  initBuffers,
  setPositionAttribute,
  setColorAttribute
} from './utils/webgl1'

const sceneDom = ref<any>(null)
const init = () => {
  const canvas = sceneDom.value
  const gl: WebGLRenderingContext = canvas.getContext('webgl')
  if (!gl) {
    console.log('webgl 初始化失败')
    return
  }
  // 使用完全不透明的黑色清除所有图像, rgba格式的颜色值，分别表示红色、绿色、蓝色和alpha通道的强度。alpha通道的强度决定了颜色的透明度，0.0表示完全透明，1.0表示完全不透明。
  // 这一行代码告诉 WebGL 如何清除颜色缓冲区。gl.COLOR_BUFFER_BIT 是一个标志，表示我们要清除颜色缓冲区。
  gl.clearColor(0.0, 0.0, 0.0, 1.0)
  // 用上面指定的颜色清除缓冲区
  gl.clear(gl.COLOR_BUFFER_BIT)
  // Vertex shader program
  const vsSource = `
    attribute vec4 aVertexPosition;
    attribute vec4 aVertexColor;

    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;

    varying lowp vec4 vColor;

    void main(void) {
      gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
      vColor = aVertexColor;
    }
  `
  const fsSource = `
  varying lowp vec4 vColor;
    void main(void) {
      gl_FragColor = vColor;
    }
  `
  const shaderProgram = initShaderProgram(gl, vsSource, fsSource)
  // 在创建着色器程序之后，我们需要查找 WebGL 返回分配的输入位置。在上述情况下，我们有一个属性和两个 Uniform 。属性从缓冲区接收值。顶点着色器的每次迭代都从分配给该属性的缓冲区接收下一个值。uniform 类似于 JavaScript 全局变量。它们在着色器的所有迭代中保持相同的值。由于属性和统一的位置是特定于单个着色器程序的，因此我们将它们存储在一起以使它们易于传递
  if (!shaderProgram) {
    console.log('创建着色器程序失败')
    return
  }
  const programInfo: any = {
    program: shaderProgram,
    attribLocations: {
      vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
      vertexColor: gl.getAttribLocation(shaderProgram, "aVertexColor"),
    },
    uniformLocations: {
      projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),
      modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
    },
  }
  const buffers = initBuffers(gl);
  if (!buffers) {
    console.log('创建缓冲区失败')
    return
  }
  // Draw the scene
  drawScene(gl, programInfo, buffers);
}
const drawScene = (gl: WebGLRenderingContext, programInfo: WebGLProgram, buffers: WebGLBuffer) => {
  // 用背景色擦除画布，接着建立摄像机透视矩阵。设置 45 度的视图角度，并且设置一个适合实际图像的宽高比。指定在摄像机距离 0.1 到 100 单位长度的范围内的物体可见。
  // 接着加载特定位置，并把正方形放在距离摄像机 6 个单位的位置。然后，我们绑定正方形的顶点缓冲到上下文，并配置好，再通过调用 drawArrays() 方法来画出对象。
  // Clear to black, fully opaque
  gl.clearColor(0.0, 0.0, 0.0, 1.0)
  // Clear everything
  gl.clearDepth(1.0)
  // Enable depth testing
  gl.enable(gl.DEPTH_TEST)
  // Near things obscure far things
  gl.depthFunc(gl.LEQUAL)
  // Clear the canvas before we start drawing on it.
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

  // Create a perspective matrix, a special matrix that is
  // used to simulate the distortion of perspective in a camera.
  // Our field of view is 45 degrees, with a width/height
  // ratio that matches the display size of the canvas
  // and we only want to see objects between 0.1 units
  // and 100 units away from the camera.
  // 创建一个透视矩阵，这是一种特殊的矩阵，用于模拟相机中的透视失真。我们的视野是 45 度，宽高比与画布的显示大小相匹配，我们只希望看到相机到 0.1 单位和 100 单位之间的物体。
  // 这个矩阵将用于将 3D 场景中的物体投影到 2D 画布上。
  const fieldOfView = (45 * Math.PI) / 180; // in radians
  // const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const aspect = innerWidth.value / innerHeight.value
  const zNear = 0.1;
  const zFar = 100.0;
  const projectionMatrix = mat4.create();
  // note: glmatrix.js always has the first argument
  // as the destination to receive the result.
  mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);

  // Set the drawing position to the "identity" point, which is
  // the center of the scene.
  const modelViewMatrix = mat4.create();
  // Now move the drawing position a bit to where we want to
  // start drawing the square.
  // 移动到我们想要开始绘制正方形的位置。
  mat4.translate(
    modelViewMatrix, // destination matrix
    modelViewMatrix, // matrix to translate
    [-0.0, 0.0, -6.0], //
  );

  // Tell WebGL how to pull out the positions from the position
  // buffer into the vertexPosition attribute.
  setPositionAttribute(gl, buffers, programInfo)
  setColorAttribute(gl, buffers, programInfo)

  // Tell WebGL to use our program when drawing

  gl.useProgram(programInfo.program)

  // Set the shader uniforms

  gl.uniformMatrix4fv(
    programInfo.uniformLocations.projectionMatrix,
    false,
    projectionMatrix,
  );
  gl.uniformMatrix4fv(
    programInfo.uniformLocations.modelViewMatrix,
    false,
    modelViewMatrix,
  );

  {
    const offset = 0;
    const vertexCount = 4;
    gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
  }
}

onMounted(() => {
  init()
})
</script>
<style lang="scss" scoped>
.scene-wrap {
  display: block;
  background-color: var(--vt-c-white);
}
</style>